﻿using AbpProject.MultiTenancy.Dto;
using Abp.Application.Services;
using System.Threading.Tasks;
using AbpProject.MultiTenancy;
using Abp.Extensions;
using Abp.Runtime.Security;
using AbpProject.Editions;
using AbpProject.Authorization.Users;
using AbpProject.Authorization.Roles;
using Abp.MultiTenancy;
using Abp.Domain.Repositories;
using System.Linq;
using Microsoft.AspNetCore.Identity;
using Abp.IdentityFramework;
using Abp.Application.Services.Dto;
using System.Net.Mail;
using System.Text;
using Abp.UI;
using System;
using AbpProject.RegisterTenants.Dto;
using Abp.Authorization.Users;
using Abp.EntityFrameworkCore;
using AbpProject.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Abp.Dependency;
using Abp.Domain.Uow;
using System.Collections.Generic;

namespace AbpProject.RegisterTenants
{
    public class RegisterTenantAppService : AsyncCrudAppService<Tenant, TenantDto, int, PagedTenantResultRequestDto, CreateTenantDto, TenantDto>, IRegisterTenantAppService, ITransientDependency
    {
        private readonly TenantManager _tenantManager;
        private readonly EditionManager _editionManager;
        private readonly UserManager _userManager;
        private readonly RoleManager _roleManager;
        private readonly IAbpZeroDbMigrator _abpZeroDbMigrator;
        private readonly IRepository<AbpRegisterCheck, long> _registerCheckRepository;
        private readonly IRepository<User, long> _userRepository;
        private readonly IDbContextProvider<AbpProjectDbContext> _dbContextProvider;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IPasswordHasher<User> _passwordHasher;
        public RegisterTenantAppService(
            IRepository<Tenant, int> repository,
            TenantManager tenantManager,
            EditionManager editionManager,
            UserManager userManager,
            RoleManager roleManager,
            IAbpZeroDbMigrator abpZeroDbMigrator,
            IRepository<AbpRegisterCheck, long> registerCheckRepository,
            IRepository<User, long> userRepository,
            IDbContextProvider<AbpProjectDbContext> dbContextProvider,
             IUnitOfWorkManager unitOfWorkManager,
             IPasswordHasher<User> passwordHasher
            )
            : base(repository)
        {
            _tenantManager = tenantManager;
            _editionManager = editionManager;
            _userManager = userManager;
            _roleManager = roleManager;
            _abpZeroDbMigrator = abpZeroDbMigrator;
            _registerCheckRepository = registerCheckRepository;
            _userRepository = userRepository;
            _dbContextProvider = dbContextProvider;
            _unitOfWorkManager = unitOfWorkManager;
            _passwordHasher = passwordHasher;
        }


        public async Task SendEmail(EntityDto<string> input)
        {

            var code = new Random().Next(0, 9999);
            var model = new AbpRegisterCheck
            {
                MobileOrEmail = input.Id,
                Code = code.ToString(),
                IsInvalid = false
            };
            await _registerCheckRepository.InsertAsync(model);
            var body = "您的验证码是：" + code;
            var subject = "ABP验证码";
            Send(subject, body, input.Id, false);
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="Subject">邮件标题</param>
        /// <param name="Body">邮件正文</param>
        /// <param name="mailTo">接收邮箱</param>
        /// <param name="IsBodyHtml">是否是HTML格式</param>
        /// <returns></returns>
        private bool Send(string Subject, string Body, string mailTo, bool IsBodyHtml = false)
        {

            string smtpServer = "smtp.163.com"; //SMTP服务器
            string mailFrom = "xxx@163.com"; //登陆用户名，邮箱
            string userPassword = "xxxx";//注意授权码  不是登录密码
            SmtpClient smtpClient = new SmtpClient();
            smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;//指定电子邮件发送方式
            smtpClient.Host = smtpServer; //指定SMTP服务器
            smtpClient.Credentials = new System.Net.NetworkCredential(mailFrom, userPassword);//用户名和密码
                                                                                              // 发送邮件设置       
            MailMessage mailMessage = new MailMessage(mailFrom, mailTo); // 发送人和收件人
            mailMessage.Subject = Subject;//主题
            mailMessage.Body = Body;//内容
            mailMessage.BodyEncoding = Encoding.UTF8;//正文编码
            mailMessage.IsBodyHtml = IsBodyHtml;//设置为HTML格式
            mailMessage.Priority = MailPriority.Low;//优先级

            try
            {
                smtpClient.Send(mailMessage); // 发送邮件
                return true;
            }
            catch (SmtpException ex)
            {
                return false;
            }
        }

        public async Task CheckCode(CheckCodeInput input)
        {
        
            List<string> list = new List<string>();
            list.Add(AbpDataFilters.MayHaveTenant);
            list.Add(AbpDataFilters.MustHaveTenant);

            using (_unitOfWorkManager.Current.DisableFilter(list.ToArray()))//禁用MayHaveTenant  MustHaveTenant过滤
            {

                var tenant = await _tenantManager.FindByTenancyNameAsync(input.TenantCode);
                if (tenant == null)
                {
                    throw new UserFriendlyException("租户不存在!");
                }

                var user = await _userManager.Users.FirstOrDefaultAsync(r => r.EmailAddress.Equals(input.MobileOrEmail) && r.TenantId == tenant.Id);
                if (user == null)
                {
                    throw new UserFriendlyException("邮箱不存在!");
                }

                var check = await _registerCheckRepository.FirstOrDefaultAsync(r => r.MobileOrEmail.Equals(input.MobileOrEmail) && r.Code.Equals(input.Code));
                if (check.IsInvalid)
                {
                    throw new UserFriendlyException("验证码已失效!");
                }

                if (check.InvalidTime.HasValue && check.InvalidTime.Value < DateTime.Now)
                {
                    throw new UserFriendlyException("验证码已过期!");
                }
              
                check.IsInvalid = true;
                await _registerCheckRepository.UpdateAsync(check);
                switch (input.Type)
                {                      
                    case "Tenant":
                        tenant.IsActive = true;
                        await _tenantManager.UpdateAsync(tenant);
                        var body = "您的管理员账号是 admin ，初始密码是 123qwe  ，登录后请及时修改密码，避免造成损失。";
                        var subject = tenant.TenancyName+ "租户激活成功";
                        Send(subject, body, input.MobileOrEmail, false);
                        break;
                    case "Reset":
                        user.Password = _passwordHasher.HashPassword(user, input.Password);
                        await _userManager.UpdateAsync(user);
                        body = "重置后密码为 " + input.Password + "";
                        subject = "密码重置成功";
                        Send(subject, body, input.MobileOrEmail, false);
                        break;
                }
            }
        }

        /// <summary>
        /// (无需权限)注册租户
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override async Task<TenantDto> Create(CreateTenantDto input)
        {
            CheckCreatePermission();

            // Create tenant
            var tenant = ObjectMapper.Map<Tenant>(input);
            tenant.ConnectionString = input.ConnectionString.IsNullOrEmpty()
                ? null
                : SimpleStringCipher.Instance.Encrypt(input.ConnectionString);

            var defaultEdition = await _editionManager.FindByNameAsync(EditionManager.DefaultEditionName);
            if (defaultEdition != null)
            {
                tenant.EditionId = defaultEdition.Id;
            }

            await _tenantManager.CreateAsync(tenant);
            await CurrentUnitOfWork.SaveChangesAsync(); // To get new tenant's id.

            // Create tenant database
            _abpZeroDbMigrator.CreateOrMigrateForTenant(tenant);

            // We are working entities of new tenant, so changing tenant filter
            using (CurrentUnitOfWork.SetTenantId(tenant.Id))
            {
                // Create static roles for new tenant
                CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id));

                await CurrentUnitOfWork.SaveChangesAsync(); // To get static role ids

                // Grant all permissions to admin role
                var adminRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Admin);
                await _roleManager.GrantAllPermissionsAsync(adminRole);

                // Create admin user for the tenant
                var adminUser = User.CreateTenantAdminUser(tenant.Id, input.AdminEmailAddress);
                await _userManager.InitializeOptionsAsync(tenant.Id);
                CheckErrors(await _userManager.CreateAsync(adminUser, User.DefaultPassword));
                await CurrentUnitOfWork.SaveChangesAsync(); // To get admin user's id

                // Assign admin user to role!
                CheckErrors(await _userManager.AddToRoleAsync(adminUser, adminRole.Name));
                await CurrentUnitOfWork.SaveChangesAsync();
            }

            return MapToEntityDto(tenant);
        }
        private void CheckErrors(IdentityResult identityResult)
        {
            identityResult.CheckErrors(LocalizationManager);
        }

    }
}
